<?php
/**
 * Created by PhpStorm.
 * User: DongYao
 * Date: 2018/2/2
 * Time: 下午10:13
 */

namespace App\Support\Device;


use App\Tables\Device;
use QrCode;
use ZipArchive;
use Illuminate\Support\Facades\Log;
use Throwable;
use Exception;
use Generator;

class QRCodeExport
{
    private $base_dir;

    /**
     * 二维码大小
     * @var int
     */
    private $size = 100;

    /**
     * 二维码中心logo
     * @var null
     */
    private $logo_file = null;

    /**
     * 二维码链接路由名称（可用于生成链接）
     * @var null
     */
    private $route_name = 'wx.device.packages';

    /**
     * 路由参数中设备号的参数名称
     * @var string
     */
    private $route_device_sn_param_name = 'device_sn';

    /**
     * 二维码链接（优先使用）
     * @var
     */
    private $url;

    /**
     * 文件压缩实例
     * @var
     */
    private $zip;

    /**
     * 文件压缩状态
     * @var
     */
    private $zip_status;

    /**
     * 获取zip文件夹名称
     * @var null
     */
    private $zip_file_dir_name = null;

    /**
     * 获取zip文件路径
     * @var null
     */
    private $zip_file_path = null;

    /**
     *
     * DeviceQRCodeExport constructor.
     */
    public function __construct()
    {
        $this->base_dir = storage_path('/app/public/device/qrcodes');
        $this->makeTmpDir();
        $this->zip = new ZipArchive();
    }

    /**
     * 导出设备二维码
     * @param Generator $devices
     * @return bool
     */
    public function exportZip(Generator $devices)
    {
        $flag = false;
        try {
            $res = $this->zip->open($this->zipFileName(), ZipArchive::CREATE);
            if ($res === true) {
                foreach ($devices as $device) {
                    if ($device instanceof Device) {
                        $qrcode_file = $this->saveQRCodeFile($device->device_sn);
                        $this->zip->addFile($qrcode_file, "./{$this->zip_file_dir_name}/" . $device->device_sn . '.png');
                    }

                    $flag = true;
                }
            } else {
                $flag = false;
            }
        } catch (Exception $exception) {
            $flag = false;
            Log::error('导出二维码压缩文件错误', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
        } catch (Throwable $exception) {
            $flag = false;
            Log::error('导出二维码压缩文件错误', [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
        }

        $this->zip_status = $this->zipStatusString();
        $this->zip->close();

        if (!file_exists($this->zip_file_path)) {
            $this->zip_status = '压缩失败';
            $flag = false;
        }

        return $flag;
    }

    /**
     * 获取zip文件名称
     * @return string
     */
    private function zipFileName()
    {
        $this->zip_file_dir_name = date('YmdHis') .random_int(100, 999) . "-{$this->size}";

        return $this->zip_file_path = $this->base_dir . $this->zip_file_dir_name . ".zip";
    }

    /**
     * 获取zip文件路径
     * @return mixed
     */
    public function getZipFilePath()
    {
        return $this->zip_file_path;
    }

    /**
     * 保存二维码文件
     * @param $device_sn
     * @param bool $is_new_file
     * @return string
     */
    public function saveQRCodeFile($device_sn, $is_new_file = false)
    {
        $file_path = $this->base_dir . "{$device_sn}_{$this->size}.png";

        if (file_exists($file_path) && $is_new_file == false) {
            return $file_path;
        }

        $qrcode = QrCode::format('png')->size($this->size)->margin(1);

        if (file_exists($this->logo_file)) {
            $qrcode = $qrcode->merge($this->logo_file, 0.2, true);
        }

        if (isset($this->url) && !empty($this->url)) {
            $url = $this->url;
        } else {
            $url = rtrim(env('APP_URL')) . route($this->route_name, [$this->route_device_sn_param_name => $device_sn], false);
        }

        $qrcode->errorCorrection('M')->generate($url, $file_path);

        return $file_path;
    }

    /**
     * @param $name
     * @param string $device_sn_param_name
     * @return $this
     */
    public function setRouteName($name, $device_sn_param_name = 'device_sn')
    {
        $this->route_name = $name;
        $this->route_device_sn_param_name = $device_sn_param_name;
        return $this;
    }

    /**
     * 设置二维码图片大小
     * @param $size
     * @return $this
     */
    public function setSize($size)
    {
        $this->size = $size;

        return $this;
    }

    /**
     * 设置二维码链接
     * @param $url
     * @return $this
     */
    public function setUrl($url)
    {
        $this->url = $url;

        return $this;
    }

    /**
     * 生成临时存储文件夹
     */
    private function makeTmpDir()
    {
        if (!file_exists($this->base_dir)) {
            @mkdir($this->base_dir, 0755, true);
        }
    }

    /**
     * 设置二维码中心Logo图片
     * @param $file_real_path
     * @return $this
     */
    public function setLogoFileRealPath($file_real_path)
    {
        if (file_exists($file_real_path)) {
            $this->logo_file = $file_real_path;
        }

        return $this;
    }

    /**
     * 获取压缩状态
     * @return mixed
     */
    public function getStatus()
    {
        return $this->zip_status;
    }

    /**
     * 格式化显示压缩状态信息
     * @return string
     */
    private function zipStatusString()
    {
        switch(strtolower($this->zip->status))
        {
            case ZipArchive::ER_OK           : return '压缩成功';
            case ZipArchive::ER_MULTIDISK    : return '不支持多磁盘zip压缩包';
            case ZipArchive::ER_RENAME       : return '重命名临时文件失败';
            case ZipArchive::ER_CLOSE        : return '关闭zip压缩包失败';
            case ZipArchive::ER_SEEK         : return '寻址失败';
            case ZipArchive::ER_READ         : return '读取失败';
            case ZipArchive::ER_WRITE        : return '写入失败';
            case ZipArchive::ER_CRC          : return 'crc 校验校验失败';
            case ZipArchive::ER_ZIPCLOSED    : return 'zip压缩包已经关闭';
            case ZipArchive::ER_NOENT        : return '没有文件';
            case ZipArchive::ER_EXISTS       : return '文件已经存在';
            case ZipArchive::ER_OPEN         : return '不能打开文件';
            case ZipArchive::ER_TMPOPEN      : return '创建临时文件失败';
            case ZipArchive::ER_ZLIB         : return 'zlib错误';
            case ZipArchive::ER_MEMORY       : return '内存分配失败';
            case ZipArchive::ER_CHANGED      : return '条目已被改变';
            case ZipArchive::ER_COMPNOTSUPP  : return '不支持的压缩方式';
            case ZipArchive::ER_EOF          : return '过早的eof';
            case ZipArchive::ER_INVAL        : return '无效的参数';
            case ZipArchive::ER_NOZIP        : return '不是一个zip压缩包';
            case ZipArchive::ER_INTERNAL     : return 'internal';
            case ZipArchive::ER_INCONS       : return 'zip压缩包不一致';
            case ZipArchive::ER_REMOVE       : return '不能移除文件';
            case ZipArchive::ER_DELETED      : return '条目已被删除';

            default: return sprintf('未知错误 %s', $this->zip->status );
        }
    }
}